﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Blade.Metadata.PeLoader {
	sealed class CliTaggedField {
		public static readonly CliTaggedField TypeDefOrRef = new CliTaggedField(2) {
			Tags = {
				{MetadataTable.TypeDef, 0},
				{MetadataTable.TypeRef, 1},
				{MetadataTable.TypeSpec, 2},
			}
		};
		public static readonly CliTaggedField HasConstant = new CliTaggedField(2) {
			Tags = {
				{MetadataTable.Field, 0},
				{MetadataTable.Param, 1},
				{MetadataTable.Property, 2},
			}
		};
		public static readonly CliTaggedField HasCustomAttribute = new CliTaggedField(5) {
			Tags = {
				{MetadataTable.MethodDef, 0},
				{MetadataTable.Field, 1},
				{MetadataTable.TypeRef, 2},
				{MetadataTable.TypeDef, 3},
				{MetadataTable.Param, 4},
				{MetadataTable.InterfaceImpl, 5},
				{MetadataTable.MemberRef, 6},
				{MetadataTable.Module, 7},
				{MetadataTable.Permission, 8},
				{MetadataTable.Property, 9},
				{MetadataTable.Event, 10},
				{MetadataTable.StandAloneSig, 11},
				{MetadataTable.ModuleRef, 12},
				{MetadataTable.TypeSpec, 13},
				{MetadataTable.Assembly, 14},
				{MetadataTable.AssemblyRef, 15},
				{MetadataTable.File, 16},
				{MetadataTable.ExportedType, 17},
				{MetadataTable.ManifestResource, 18},
			}
		};
		public static readonly CliTaggedField HasFieldMarshal = new CliTaggedField(1) {
			Tags = {
				{MetadataTable.Field, 0},
				{MetadataTable.Param, 1},
			}
		};
		public static readonly CliTaggedField HasDeclSecurity = new CliTaggedField(2) {
			Tags = {
				{MetadataTable.TypeDef, 0},
				{MetadataTable.MethodDef, 1},
				{MetadataTable.Assembly, 2},
			}
		};
		public static readonly CliTaggedField MemberRefParent = new CliTaggedField(3) {
			Tags = {
				{MetadataTable.TypeDef, 0},
				{MetadataTable.TypeRef, 1},
				{MetadataTable.ModuleRef, 2},
				{MetadataTable.MethodDef, 3},
				{MetadataTable.TypeSpec, 4},
			}
		};
		public static readonly CliTaggedField HasSemantics = new CliTaggedField(1) {
			Tags = {
				{MetadataTable.Event, 0},
				{MetadataTable.Property, 1},
			}
		};
		public static readonly CliTaggedField MethodDefOrRef = new CliTaggedField(1) {
			Tags = {
				{MetadataTable.MethodDef, 0},
				{MetadataTable.MemberRef, 1},
			}
		};
		public static readonly CliTaggedField MemberForwarded = new CliTaggedField(1) {
			Tags = {
				{MetadataTable.Field, 0},
				{MetadataTable.MethodDef, 1},
			}
		};
		public static readonly CliTaggedField Implementation = new CliTaggedField(2) {
			Tags = {
				{MetadataTable.File, 0},
				{MetadataTable.AssemblyRef, 1},
				{MetadataTable.ExportedType, 2},
			}
		};
		public static readonly CliTaggedField CustomAttributeType = new CliTaggedField(3) {
			Tags = {
				//{null, 0},
				//{null, 1},
				{MetadataTable.MethodDef, 2},
				{MetadataTable.MemberRef, 3},
				//{null, 4},
			}
		};
		public static readonly CliTaggedField ResolutionScope = new CliTaggedField(2) {
			Tags = {
				{MetadataTable.Module, 0},
				{MetadataTable.ModuleRef, 1},
				{MetadataTable.AssemblyRef, 2},
				{MetadataTable.TypeRef, 3},
			}
		};
		public static readonly CliTaggedField TypeOrMethodDef = new CliTaggedField(1) {
			Tags = {
				{MetadataTable.TypeDef, 0},
				{MetadataTable.MethodDef, 1},
			}
		};

		private CliTaggedField(int tagBits) {
			this.TagBits = tagBits;
		}

		public int TagBits { get; private set; }

		public Dictionary<MetadataTable, int> Tags { get { return m_Tags; } }
		private Dictionary<MetadataTable, int> m_Tags = new Dictionary<MetadataTable, int>();

		internal int GetSize(PeFile peFile) {
			int maxRowsForShort = (1 << (16 - TagBits)) - 1;
			foreach (var item in Tags.Keys) {
				if (peFile.cliMetadataHeader.Tables[item].RowCount > maxRowsForShort)
					return 4;
			}
			return 2;
		}
	}
}
